/*
 * 描          述:  <描述>
 * 修  改   人:  Administrator
 * 修改时间:  2019年5月3日
 * <修改描述:>
 */
package com.tx.component.basicdata.util;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.tx.component.basicdata.annotation.BasicDataEntity;
import com.tx.component.basicdata.model.BasicData;
import com.tx.component.basicdata.model.DataDict;
import com.tx.core.exceptions.util.AssertUtils;
import com.tx.core.paged.model.PagedList;
import com.tx.core.paged.model.impl.PagedListImpl;
import com.tx.core.util.ObjectUtils;
import com.tx.core.util.TableInfoUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.core.convert.TypeDescriptor;

import javax.persistence.Transient;
import java.beans.PropertyDescriptor;
import java.util.*;
import java.util.Map.Entry;

/**
 * 基础数据工具类<br/>
 * <功能详细描述>
 *
 * @author Administrator
 * @version [版本号, 2019年5月3日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class BasicDataUtils {

    /**
     * 将数据字典类型的分页列表转换为指定基本数据类型BD的分页列表。<br/>
     * 该方法通过遍历数据字典类型的分页列表中的每个元素，并使用fromDataDict方法将其转换为指定类型BD，
     * 然后将转换后的元素添加到新的分页列表中。
     *
     * @param dataPagedList 数据字典类型的分页列表，不可为null。
     * @param type          指定的基本数据类型BD的Class对象，不可为null，并且必须是BasicData类型的子类。
     * @return 转换后的指定类型BD的分页列表。如果输入的dataPagedList为null，则返回null。
     * @throws IllegalArgumentException 如果type为null，或者type不是BasicData的子类，则抛出此异常。
     * @see #fromDataDict(DataDict, Class)
     */
    public static <BD extends BasicData> PagedList<BD> fromDataDictPagedList(
            PagedList<DataDict> dataPagedList, Class<BD> type) {
        AssertUtils.notNull(type, "type is null."); // 确保类型不为null
        AssertUtils.isTrue(BasicData.class.isAssignableFrom(type),
                "type:{} is not assignableFrom BasicData.",
                new Object[]{type}); // 确保类型是BasicData的子类

        if (dataPagedList == null) {
            return null; // 如果输入的分页列表为null，直接返回null
        }

        PagedList<BD> pagedList = new PagedListImpl<>(); // 创建新的分页列表实例
        // 填充新分页列表的基本信息
        pagedList.setCount(dataPagedList.getCount());
        pagedList.setPageIndex(dataPagedList.getPageIndex());
        pagedList.setPageSize(dataPagedList.getPageSize());

        List<BD> objList = new ArrayList<>(); // 创建转换后元素的列表
        pagedList.setList(objList); // 将转换后元素的列表设置到分页列表中

        // 遍历原始分页列表中的每个元素，转换并添加到新的分页列表中
        for (DataDict dataTemp : dataPagedList.getList()) {
            objList.add(fromDataDict(dataTemp, type));
        }
        return pagedList; // 返回转换后的分页列表
    }

    /**
     * 将DataDict列表转换为指定类型的对象列表。<br/>
     * 该方法通过遍历DataDict列表，将每个DataDict对象转换为指定的BD类型（BD继承自BasicData基类）的对象，并将这些对象添加到返回的列表中。
     *
     * @param dataList 要转换的DataDict列表。如果此列表为null，则返回null。
     * @param type     指定的BD类型，该类型必须继承自BasicData。不能为null，且必须是BasicData的子类。
     * @return 返回一个BD类型的对象列表，其中每个对象都是根据给定的DataDict对象和类型参数转换而来的。如果输入的dataList为null，则返回null。
     * @throws IllegalArgumentException 如果type为null，或者type不是BasicData的子类，则抛出此异常。
     * @see [类、类#方法、类#成员] 可以参考本类中的其他方法以及BasicData和DataDict类的相关成员。
     */
    public static <BD extends BasicData> List<BD> fromDataDictList(
            List<DataDict> dataList, Class<BD> type) {
        // 校验类型参数type，确保其不为null且是BasicData的子类
        AssertUtils.notNull(type, "type is null.");
        AssertUtils.isTrue(BasicData.class.isAssignableFrom(type),
                "type:{} is not assignableFrom BasicData.",
                new Object[]{type});
        // 如果dataList为null，直接返回null
        if (dataList == null) {
            return null;
        }

        // 创建一个BD类型对象的列表，用于存放转换后的对象
        List<BD> objList = new ArrayList<>();
        // 遍历dataList，将每个DataDict对象转换为BD类型对象，并添加到objList中
        for (DataDict dataTemp : dataList) {
            objList.add(fromDataDict(dataTemp, type));
        }
        // 返回转换后的对象列表
        return objList;
    }

    /**
     * 根据数据字典生成基础数据对象。<br/>
     * 该方法通过遍历数据字典中的属性，将它们的值赋给创建的基础数据对象实例。
     *
     * @param data 数据字典，提供数据给基础数据对象。
     * @param type 基础数据对象的类型，必须是BasicData的子类。
     * @return 返回根据数据字典创建的基础数据对象实例。如果输入的数据字典为null，则返回null。
     * @throws IllegalArgumentException 如果指定的类型为null或不是BasicData的子类，则抛出此异常。
     * @see PropertyAccessorFactory#forBeanPropertyAccess(Object) 用于获取Bean的属性访问器。
     */
    public static <BD extends BasicData> BD fromDataDict(DataDict data,
                                                         Class<BD> type) {
        // 确保类型不为空且是BasicData的子类
        AssertUtils.notNull(type, "type is null.");
        AssertUtils.isTrue(BasicData.class.isAssignableFrom(type),
                "type:{} is not assignableFrom BasicData.",
                new Object[]{type});
        if (data == null) {
            return null;
        }

        // 创建基础数据对象实例
        BD bd = ObjectUtils.newInstance(type);
        BeanWrapper bdBW = PropertyAccessorFactory.forBeanPropertyAccess(bd);

        // 获取数据字典的属性访问器
        BeanWrapper dataBW = PropertyAccessorFactory
                .forBeanPropertyAccess(data);
        // 遍历数据字典的属性，将符合条件的属性值赋给基础数据对象
        for (PropertyDescriptor pd : dataBW.getPropertyDescriptors()) {
            String property = pd.getName();
            // 跳过不可读写、特殊属性名的属性
            if (pd.getReadMethod() == null || pd.getWriteMethod() == null
                    || "attributes".equals(property)
                    || "attributeJSONObject".equals(property)) {
                continue;
            }
            // 获取属性值并赋值给基础数据对象
            Object value = dataBW.getPropertyValue(property);

            PropertyDescriptor bdBWPDTemp = bdBW
                    .getPropertyDescriptor(property);
            // 跳过基础数据对象中不可写属性
            if (bdBWPDTemp == null || bdBWPDTemp.getWriteMethod() == null) {
                continue;
            }
            bdBW.setPropertyValue(property, value);
        }
        // 处理数据字典中额外的属性，通过JSONObject形式
        if (!MapUtils.isEmpty(data.getAttributeJSONObject())) {
            for (Entry<String, Object> entryTemp : data.getAttributeJSONObject()
                    .entrySet()) {
                String key = entryTemp.getKey();
                Object value = entryTemp.getValue();

                PropertyDescriptor bdBWPDTemp = bdBW.getPropertyDescriptor(key);
                // 跳过基础数据对象中不可写属性
                if (bdBWPDTemp == null || bdBWPDTemp.getWriteMethod() == null) {
                    continue;
                }
                bdBW.setPropertyValue(key, value);
            }
        }
        return bd;
    }

    /**
     * 将对象转换为Map形式的数据字典<br/>
     * 该方法会遍历指定对象的所有属性，将可读且非临时的属性转换为键值对形式存储在DataDict中。
     * 如果属性值不为null，则属性名作为键，属性值作为值。对于DataDict对象，还会尝试直接设置属性值；
     * 对于无法直接设置的属性，则将其存储在attributeJSONObject中。
     *
     * @param object 需要转换的对象，其类型必须继承自BasicData。
     * @return DataDict 转换后的数据字典对象，如果输入对象为null，则返回null。
     * @see DataDict 用于存储转换后数据的类。
     */
    public static <BD extends BasicData> DataDict toDataDict(BD object) {
        if (object == null) {
            return null;
        }

        // 初始化结果映射，用于存储对象属性名和其对应的值
        Map<String, Object> resMap = new HashMap<>();
        BeanWrapper objBW = PropertyAccessorFactory
                .forBeanPropertyAccess(object);
        // 遍历对象所有属性，筛选出可读写且非临时的属性，忽略标注了JsonIgnore或Transient的属性
        for (PropertyDescriptor pdTemp : objBW.getPropertyDescriptors()) {
            if (pdTemp.getReadMethod() == null
                    || pdTemp.getWriteMethod() == null) {
                continue;
            }
            String property = pdTemp.getName();
            TypeDescriptor tdTemp = objBW.getPropertyTypeDescriptor(property);
            if (tdTemp.hasAnnotation(JsonIgnore.class)) {
                continue;
            }
            if (tdTemp.hasAnnotation(Transient.class)) {
                continue;
            }

            // 获取属性值并添加到结果映射中
            Object value = pdTemp.getValue(property);
            if (value == null) {
                continue;
            }
            resMap.put(property, value);
        }

        // 初始化DataDict对象，用于存储最终结果
        DataDict data = new DataDict();
        BeanWrapper ddBW = PropertyAccessorFactory.forBeanPropertyAccess(data);
        // 将结果映射中的数据设置到DataDict对象中
        for (Entry<String, Object> entryTemp : resMap.entrySet()) {
            String key = entryTemp.getKey();
            // 如果DataDict对象支持直接设置属性，则直接设置；否则存入attributeJSONObject
            if (ddBW.isWritableProperty(key)) {
                ddBW.setPropertyValue(key, entryTemp.getValue());
            } else {
                data.getAttributeJSONObject()
                        .put(key, Objects.toString(entryTemp.getValue()));
            }
        }

        return data;
    }


    /**
     * 将对象列表转换为DataDict列表<br/>
     * 该方法通过遍历给定的对象列表（这些对象需继承自BasicData），将每个对象转换为一个DataDict对象，并将转换后的对象添加到一个DataDict列表中。<br/>
     *
     * @param objectList 需要被转换的对象列表，列表中的元素必须继承自BasicData。
     * @return 返回一个包含转换后的DataDict对象的列表。如果输入的对象列表为null，则返回null。
     * @return List<DataDict> 转换后的数据字典列表。
     */
    public static <BD extends BasicData> List<DataDict> toDataDictList(
            List<BD> objectList) {
        // 检查输入的对象列表是否为null
        if (objectList == null) {
            return null;
        }

        // 创建一个空的DataDict列表用于存放转换后的对象
        List<DataDict> ddList = new ArrayList<>();
        // 遍历对象列表，将每个对象转换为DataDict并添加到ddList中
        for (BD object : objectList) {
            ddList.add(toDataDict(object));
        }
        // 返回转换后的DataDict列表
        return ddList;
    }


    /**
     * 将对象列表转换为数据字典列表的分页形式<br/>
     * 该方法用于将泛型BD（继承自BasicData基类）的对象列表，转换为DataDict类型的分页列表。适用于需要将具体业务对象转换为更通用的数据表示形式的场景。
     *
     * @param objectPagedList 需要转换的原始对象的分页列表，类型为泛型BD，继承自BasicData。
     * @return 返回转换后的数据字典分页列表，类型为PagedList<DataDict>。
     */
    public static <BD extends BasicData> PagedList<DataDict> toDataDictPagedList(
            PagedList<BD> objectPagedList) {
        if (objectPagedList == null) {
            return null;
        }

        // 创建一个新的分页列表实例，用于存放转换后的数据字典对象
        PagedList<DataDict> pagedList = new PagedListImpl<>();
        // 设置新分页列表的基本信息，如总数、页码和每页大小等
        pagedList.setCount(objectPagedList.getCount());
        pagedList.setPageIndex(objectPagedList.getPageIndex());
        pagedList.setPageSize(objectPagedList.getPageSize());

        List<DataDict> ddList = new ArrayList<>();
        pagedList.setList(ddList);

        // 遍历原始对象列表，将每个对象转换为数据字典，并添加到新的分页列表中
        for (BD object : objectPagedList.getList()) {
            ddList.add(toDataDict(object));
        }
        return pagedList;
    }


    //    /**
    //     * 将对象转换为Map<br/>
    //     * <功能详细描述>
    //     * @return [参数说明]
    //     * 
    //     * @return Map<String,Object> [返回类型说明]
    //     * @exception throws [异常类型] [异常说明]
    //     * @see [类、类#方法、类#成员]
    //     */
    //    public static <BD extends BasicData> PagedList<List<Map<String, Object>>> toMapPagedList(
    //            PagedList<BD> objectPagedList) {
    //        if (objectPagedList == null) {
    //            return null;
    //        }
    //        
    //        PagedList<List<Map<String, Object>>> pagedList = new PagedList<>();
    //        pagedList.setCount(objectPagedList.getCount());
    //        pagedList.setPageIndex(objectPagedList.getPageIndex());
    //        pagedList.setPageSize(objectPagedList.getPageSize());
    //        pagedList.setQueryPageSize(objectPagedList.getQueryPageSize());
    //        List<Map<String, Object>> mapList = new ArrayList<>();
    //        pagedList.setList(mapList);
    //        
    //        for (BD object : objectPagedList.getList()) {
    //            mapList.add(toMap(object));
    //        }
    //        return pagedList;
    //    }
    //    
    //    /**
    //     * 将对象转换为Map<br/>
    //     * <功能详细描述>
    //     * @return [参数说明]
    //     * 
    //     * @return Map<String,Object> [返回类型说明]
    //     * @exception throws [异常类型] [异常说明]
    //     * @see [类、类#方法、类#成员]
    //     */
    //    public static <BD extends BasicData> List<Map<String, Object>> toMapList(
    //            List<BD> objectList) {
    //        if (objectList == null) {
    //            return null;
    //        }
    //        
    //        List<Map<String, Object>> mapList = new ArrayList<>();
    //        for (BD object : objectList) {
    //            mapList.add(toMap(object));
    //        }
    //        return mapList;
    //    }
    //    
    //    /**
    //     * 将对象转换为Map<br/>
    //     * <功能详细描述>
    //     * @return [参数说明]
    //     * 
    //     * @return Map<String,Object> [返回类型说明]
    //     * @exception throws [异常类型] [异常说明]
    //     * @see [类、类#方法、类#成员]
    //     */
    //    public static <BD extends BasicData> Map<String, Object> toMap(BD object) {
    //        if (object == null) {
    //            return null;
    //        }
    //        
    //        Map<String, Object> resMap = new HashMap<>();
    //        BeanWrapper objBW = PropertyAccessorFactory
    //                .forBeanPropertyAccess(object);
    //        for (PropertyDescriptor pdTemp : objBW.getPropertyDescriptors()) {
    //            if (pdTemp.getReadMethod() == null
    //                    || pdTemp.getWriteMethod() == null) {
    //                continue;
    //            }
    //            String property = pdTemp.getName();
    //            TypeDescriptor tdTemp = objBW.getPropertyTypeDescriptor(property);
    //            if (tdTemp.hasAnnotation(JsonIgnore.class)) {
    //                continue;
    //            }
    //            if (tdTemp.hasAnnotation(Transient.class)) {
    //                continue;
    //            }
    //            
    //            Object value = pdTemp.getValue(property);
    //            if (value == null) {
    //                continue;
    //            }
    //            resMap.put(property, value);
    //        }
    //        return resMap;
    //    }
    //    
    //    /**
    //     * 将对象转换为Map<br/>
    //     * <功能详细描述>
    //     * @return [参数说明]
    //     * 
    //     * @return Map<String,Object> [返回类型说明]
    //     * @exception throws [异常类型] [异常说明]
    //     * @see [类、类#方法、类#成员]
    //     */
    //    public static <BD extends BasicData> PagedList<BD> fromMapPagedList(
    //            PagedList<Map<String, Object>> mapPagedList, Class<BD> type) {
    //        AssertUtils.notNull(type, "type is null.");
    //        AssertUtils.isTrue(BasicData.class.isAssignableFrom(type),
    //                "type:{} is not assignableFrom BasicData.",
    //                new Object[] { type });
    //        if (mapPagedList == null) {
    //            return null;
    //        }
    //        
    //        PagedList<BD> pagedList = new PagedList<>();
    //        pagedList.setCount(mapPagedList.getCount());
    //        pagedList.setPageIndex(mapPagedList.getPageIndex());
    //        pagedList.setPageSize(mapPagedList.getPageSize());
    //        pagedList.setQueryPageSize(mapPagedList.getQueryPageSize());
    //        List<BD> objList = new ArrayList<>();
    //        pagedList.setList(objList);
    //        for (Map<String, Object> mapTemp : mapPagedList.getList()) {
    //            objList.add(fromMap(mapTemp, type));
    //        }
    //        return pagedList;
    //    }
    //    
    //    /**
    //     * 从MapList转换为实例List
    //     * <功能详细描述>
    //     * @param mapList
    //     * @param type
    //     * @return [参数说明]
    //     * 
    //     * @return List<BD> [返回类型说明]
    //     * @exception throws [异常类型] [异常说明]
    //     * @see [类、类#方法、类#成员]
    //     */
    //    public static <BD extends BasicData> List<BD> fromMapList(
    //            List<Map<String, Object>> mapList, Class<BD> type) {
    //        AssertUtils.notNull(type, "type is null.");
    //        AssertUtils.isTrue(BasicData.class.isAssignableFrom(type),
    //                "type:{} is not assignableFrom BasicData.",
    //                new Object[] { type });
    //        if (mapList == null) {
    //            return null;
    //        }
    //        
    //        List<BD> objList = new ArrayList<>();
    //        for (Map<String, Object> mapTemp : mapList) {
    //            objList.add(fromMap(mapTemp, type));
    //        }
    //        return objList;
    //    }
    //    
    //    /**
    //     * 生成基础数据对象<br/>
    //     * <功能详细描述>
    //     * @param map
    //     * @param type
    //     * @return [参数说明]
    //     * 
    //     * @return BD [返回类型说明]
    //     * @exception throws [异常类型] [异常说明]
    //     * @see [类、类#方法、类#成员]
    //     */
    //    public static <BD extends BasicData> BD fromMap(Map<String, Object> map,
    //            Class<BD> type) {
    //        AssertUtils.notNull(type, "type is null.");
    //        AssertUtils.isTrue(BasicData.class.isAssignableFrom(type),
    //                "type:{} is not assignableFrom BasicData.",
    //                new Object[] { type });
    //        if (map == null) {
    //            return null;
    //        }
    //        
    //        BD bd = ObjectUtils.newInstance(type);
    //        BeanWrapper objBW = PropertyAccessorFactory.forBeanPropertyAccess(bd);
    //        
    //        for (Entry<String, Object> entryTemp : map.entrySet()) {
    //            String key = entryTemp.getKey();
    //            PropertyDescriptor pdTemp = objBW.getPropertyDescriptor(key);
    //            //            TypeDescriptor tdTemp = objBW.getPropertyTypeDescriptor(key);
    //            if (pdTemp == null || pdTemp.getReadMethod() == null
    //                    || pdTemp.getWriteMethod() == null) {
    //                continue;
    //            }
    //            //            if (tdTemp.hasAnnotation(JsonIgnore.class)) {
    //            //                continue;
    //            //            }
    //            
    //            Object value = entryTemp.getValue();
    //            objBW.setPropertyValue(key, value);
    //        }
    //        return bd;
    //    }

    /**
     * 获取基础数据类型的编码
     * 该方法用于根据给定的基础数据实体类，获取其对应的编码。首先使用类的简单名称作为编码，
     * 如果该类上标注了BasicDataEntity注解并且注解中的type字段不为空，则使用注解中指定的编码。
     *
     * @param entityClass 基础数据实体类的Class对象，不能为空。
     * @return 返回基础数据类型的编码，为字符串类型。
     * @see BasicDataEntity 注解用于标记基础数据实体类，并可指定类型的编码。
     */
    public static String getType(Class<? extends BasicData> entityClass) {
        AssertUtils.notNull(entityClass, "entityClass is null."); // 确保传入的类对象不为null

        String code = entityClass.getSimpleName(); // 默认使用类的简单名称作为编码
        // 检查类是否标注了BasicDataEntity注解，并且注解中的type字段不为空
        if (entityClass.isAnnotationPresent(BasicDataEntity.class)
                && StringUtils.isNotEmpty(
                entityClass.getAnnotation(BasicDataEntity.class)
                        .type())) {
            // 如果满足条件，则使用注解中指定的编码
            code = entityClass.getAnnotation(BasicDataEntity.class).type();
        }
        return code; // 返回计算得到的编码
    }


    /**
     * 获取对应的表名
     * 该方法用于根据传入的实体类获取其对应的数据库表名。首先尝试获取Mybatis-Plus的TableName注解指定的表名，若不存在则尝试获取JPA的@Table或@Entity注解指定的表名。如果这些注解都不存在，则默认根据实体类的类名生成表名。
     *
     * @param entityClass 实体类的Class对象，不可为null。
     * @return 返回对应实体类的表名。
     * @throws IllegalArgumentException 如果entityClass为null，抛出此异常。
     * @see com.baomidou.mybatisplus.core.metadata.TableInfo
     */
    public static String getTableName(Class<? extends BasicData> entityClass) {
        AssertUtils.notNull(entityClass, "entityClass is null.");

        return TableInfoUtils.parseTableName(entityClass);
    }
}
